/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "imgops/skin/AdaptiveSkinRecNBayesOp.h"
#include "common/utility_data_structures.h"
#include "factory/factories.h"

#include <map>
#include <string>

using namespace std;
using namespace cv;

namespace NAMESPACE{

// ****** Functor implementation ***********************************************
bool AdaptiveSkinRecNBayesFunc::operator()(const cv::Mat& src,
                                           const NormalBayesModel& model_off,
                                           const NormalBayesModel& model_on,
                                           cv::Mat& out_mask,
                                           double gain,
                                           CVT_SPACE cvt_space,
                                           int _class){
    adaptive_skin_recognition_nbayes(src, model_off, model_on, out_mask, gain, cvt_space, _class);
    return true;
}
// -----------------------------------------------------------------------------

// ****** Constants assignment *************************************************
const size_t AdaptiveSkinRecNBayesOp::MIN_INPUT_NUM = 3;
const size_t AdaptiveSkinRecNBayesOp::MIN_CRITERIA_NUM = 2;
const size_t AdaptiveSkinRecNBayesOp::MIN_OUTPUT_NUM = 1;
// -----------------------------------------------------------------------------

// ****** Operation implementation *********************************************
AdaptiveSkinRecNBayesOp::~AdaptiveSkinRecNBayesOp(){}
AdaptiveSkinRecNBayesOp::AdaptiveSkinRecNBayesOp() : AbstractOperation<AdaptiveSkinRecNBayesFunc>(){

    min_input_num = MIN_INPUT_NUM;
    min_criteria_num = MIN_CRITERIA_NUM;
    min_output_num = MIN_OUTPUT_NUM;

    k1.set_key(INPUT_NS, "img"); k1.setType(TypeParseTraits<cv::Mat*>::name());
    k2.set_key(INPUT_NS,"model.stat.bayes.off"); k2.setType(TypeParseTraits<NormalBayesModel*>::name());
    k3.set_key(INPUT_NS,"model.stat.bayes.on");  k3.setType(TypeParseTraits<NormalBayesModel*>::name());

    k4.set_key(OUTPUT_NS, "mask"); k4.setType(TypeParseTraits<cv::Mat*>::name());

    k5.set_key(CRITERIA_NS, "value.gain");        k5.setType(TypeParseTraits<double>::name());
    PDLRange k5_range(0, 1, 0.01);                k5.setAcceptedValues(k5_range);
    
    k6.set_key(CRITERIA_NS, "enum.color_space");  k6.setType(TypeParseTraits<CVT_SPACE*>::name());
    PDLComposite k6_comp; PDLPair k6p1("HLS", 0); PDLPair k6p2("HSV", 1); PDLPair k6p3("YCrCb", 2);
    k6_comp.add_element(k6p1).add_element(k6p2).add_element(k6p3);
    k6.setAcceptedValues(k6_comp);
    
    k7.set_key(CRITERIA_NS, "value.class");       k7.setType(TypeParseTraits<int>::name());
    PDLComposite k7_comp; PDLPair k7p1("Skin", 0); PDLPair k7p2("Non skin", 1);
    k7_comp.add_element(k7p1).add_element(k7p2);
    k7.setAcceptedValues(k7_comp);

    input_keys->push_back(k1);
    input_keys->push_back(k2);
    input_keys->push_back(k3);

    output_keys->push_back(k4);

    criteria_keys->push_back(k5);
    criteria_keys->push_back(k6);
    criteria_keys->push_back(k7);
}
AdaptiveSkinRecNBayesOp::AdaptiveSkinRecNBayesOp(const AdaptiveSkinRecNBayesOp& gbo) : AbstractOperation<AdaptiveSkinRecNBayesFunc>(gbo){}
AdaptiveSkinRecNBayesOp& AdaptiveSkinRecNBayesOp::operator=(const AdaptiveSkinRecNBayesOp& gbo){
    if(this == &gbo) return *this;
    AbstractOperation<AdaptiveSkinRecNBayesFunc>::operator=(gbo);
    return *this;
}

bool AdaptiveSkinRecNBayesOp::doOperation(const std::list<input_t>& inputs,
                                    const std::list<criteria_t>& operationCriteria,
                                    std::list<output_t>& outputs) throw(bad_io_mapping) {

    AbstractOperation<AdaptiveSkinRecNBayesFunc>::prepareIO(inputs, operationCriteria, outputs);

    Mat* in_img  = boost::any_cast<Mat*>( ((*input_mapping) [k1])->getElement() );
    if (in_img->empty()) return false;
    NormalBayesModel* model_off = boost::any_cast<NormalBayesModel*>( ((*input_mapping)[k2])->getElement() );
    NormalBayesModel* model_on  = boost::any_cast<NormalBayesModel*>( ((*input_mapping)[k3])->getElement() );

    Mat* out_mask_img = boost::any_cast<Mat*>( ((*output_mapping)[k4])->getElement() );

    double gain = boost::any_cast<double>( ((*criteria_mapping)[k5])->getElement() );
    CVT_SPACE* cvt_space  = boost::any_cast<CVT_SPACE*>( ((*criteria_mapping)[k6])->getElement() );

    // Optional parameters
    IOElement* io_class = (*criteria_mapping)[k7];
    int _skin = 1;
    int* _class = io_class ? boost::any_cast<int*>( io_class->getElement() ) : &_skin;

    (*operation)(*in_img,
                 *model_off,
                 *model_on,
                 *out_mask_img,
                 gain,
                 *cvt_space,
                 *_class);

    return true;
}

bool AdaptiveSkinRecNBayesOp::operator==(const AbstractOperation<operation_t>& op) const{
    return AbstractOperation<AdaptiveSkinRecNBayesFunc>::operator==(op);
}

bool AdaptiveSkinRecNBayesOp::operator!=(const AbstractOperation<operation_t>& op) const{
    return !operator==(op);
}
// -----------------------------------------------------------------------------

REGISTER_PARSE_TYPE(AdaptiveSkinRecNBayesOp);
SimpleFactoryImpl<PipeStepComputation, AdaptiveSkinRecNBayesOp> adaptiveSkinRecNBayesOpFactory_instance;
}
